/*
 * @Author: yayalee yangcheng960813@163.com
 * @Date: 2024-05-16 22:39:53
 * @LastEditTime: 2024-07-12 11:38:48
 * @LastEditors: yayalee yangcheng960813@163.com
 * @Description: 
 * @FilePath: \unfinished-challenges\assets\core_ts\ArrayUtil.ts
 * 可以输入预定的版权声明、个性签名、空行等
 */

import { equal, inRange, isArray, isFunction, isObject, isString } from "./TsUtil";

export class ArrayUtil {

    /**
     * @description: 数组去重
     * @param array 数组集合对象
     * @param predicate 
     * @return {*} 返回一个新的数组
     * @author: yayalee
     * @since: v1.0.0
     * ```
     * unique([1,2,1,2,1,2]);
     * //=> [1,2]
     * unique([1,2,1,2,"11","11",{a:1},{b:1},true,false,true]);
     * //=> [1,2,"11",{a:1},{b:1},true,false]
     * //对象是引用类型的只能加谓语来限制
     * unique([1,2,1,2,"11","11",{a:1},{a:1},{b:1},true,false,true],"a");
     * //=> [1,2,"11",{a:1},{b:1},true,false]
     * unique([1,2,1,2,"11","11",{a:1},{a:1},{b:1},true,false,true],item=>isObject(item)?item["a"]:item);
     * //=> [1,2,"11",{a:1},{b:1},true,false]
     * ```
     */
    public static unique<T>(array: Array<T>, predicate?: number): T[];
    public static unique<T>(array: Array<T>, predicate?: string): T[];
    public static unique<T>(array: Array<T>, predicate?: (item: T) => string): T[];
    public static unique<T>(array: Array<T>, predicate?: any): T[] {
        if (isString(predicate)) {
            const propName = predicate;
            predicate = (item: T | any) => isObject(item) ? item[propName] : item;
        }
        if (!isFunction(predicate)) {
            predicate = (item: T) => item;
        }
        const map = new Map();
        let arr = array.filter((value) => {
            let key = `${predicate(value)}_${typeof value}`;
            return (!map.has(key) || !equal(map.get(key), key)) && map.set(key, key);
        });
        // console.log(map);
        return arr;
    }

    /**
     * @description: 数组平铺/展平
     * @param {Array} array 需要平铺展平的数组
     * @param {number} depth 递归的深度
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static flatten<T>(array: Array<T>, depth: number = Infinity): T[] {
        //判断数组是否已经平铺到指定深度
        if (equal(depth, 0)) {
            return array;
        }
        //用于存储平铺后的结果
        let result: T | any[] = [];
        for (let i = 0; i < array.length; i++) {
            const item = array[i];
            if (isArray(item)) {
                const flattened = this.flatten(item as [], depth - 1);
                result = result.concat(flattened);
            } else {
                result.push(item);
            }
        }
        return result;
    }

    /**
     * @description: 随机打乱数组 es5 写法
     * @param {Array} arr
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static shuffle<T>(arr: Array<T>): T[] {
        var i = arr.length, t: T, j: number;
        while (--i) {
            j = Math.floor(Math.random() * i);
            t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
        return arr;
    }

    /**
     * @description: 删除指定索引处的数组项。
     * @param {Array} array
     * @param {number} index
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static removeAt<T>(array: Array<T>, index: number): Array<T> {
        array.splice(index, 1);
        return array;
    }

    /**
     * @description: 删除指定索引处的数组项。这样更快，但是数组的顺序会改变。
     * @param {Array} array
     * @param {number} index
     * @author: yayalee
     * @since: v1.0.0
     */
    public static fastRemoveAt<T>(array: Array<T>, index: number) {
        var length = array.length;
        if (index < 0 || index >= length) {
            return;
        }
        array[index] = array[length - 1];
        array.length = length - 1;
    }

    /**
     * @description: 从数组中删除特定对象的第一个出现项。这样更快，但是数组的顺序会改变。
     * @param {Array} array
     * @param {T} value
     * @author: yayalee
     * @since: v1.0.0
     */
    public static fastRemove<T>(array: Array<T>, value: T) {
        var index = array.indexOf(value);
        if (index >= 0) {
            array[index] = array[array.length - 1];
            --array.length;
        }
    }

    /**
     * @description: 从数组中删除特定对象的第一个出现项。
     * @param {Array} array
     * @param {T} value
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static remove<T>(array: Array<T>, value: T): boolean {
        var index = array.indexOf(value);
        if (index >= 0) {
            ArrayUtil.removeAt(array, index);
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * @description: 验证数组的类型
     * @param {Array} array
     * @param {Function} type
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static verifyType<T>(array: Array<T>, type: Function): boolean {
        if (array && array.length > 0) {
            for (var i = 0; i < array.length; i++) {
                if (!(array[i] instanceof type)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * @description: 从数组中移除minusArr中的所有值。对于minusArr中的每个值，数组中第一个匹配的实例将被删除。
     * @param {Array} array Source Array
     * @param {Array} minusArr minus Array
     * @author: yayalee
     * @since: v1.0.0
     */
    public static removeArray<T>(array: Array<T>, minusArr: Array<T>) {
        for (var i = 0, l = minusArr.length; i < l; i++) {
            ArrayUtil.remove(array, minusArr[i]);
        }
    }

    /**
     * @description: 在索引处插入一些对象
     * @param {Array} array
     * @param {Array} addObjs
     * @param {number} index
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static appendObjectsAt<T>(array: Array<T>, addObjs: Array<T>, index: number): Array<any> {
        //@ts-ignore
        array.splice.apply(array, [index, 0].concat(addObjs));
        return array;
    }


    /**
     * @description: 确定数组是否包含特定的值。
     * @param {Array} array
     * @param {T} value
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static contains<T>(array: Array<T>, value: T): boolean {
        return array.indexOf(value) >= 0;
    }

    /**
     * @description: 在数组中添加某个元素
     * @param {T} arr
     * @param {T} ele
     * @param {boolean} canRepeat 是否可重复添加相同元素 默认false
     * @return {boolean} 是否执行了添加行为
     * @author: yayalee
     * @since: v1.0.0
     */
    public static addArray<T>(arr: T[], ele: T, canRepeat: boolean = false): boolean {
        if (!canRepeat && ArrayUtil.contains(arr, ele)) {
            return false;
        }
        arr.push(ele);
        return true;
    }

    /**
     * @description: 将一个数组的项复制到一个新数组(其性能优于 Array.slice)
     * @param {Array} array
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static copyArray<T>(array: Array<T>): Array<T> {
        var len = array.length, arr_clone = new Array(len);
        for (let i = 0; i < len; i += 1)
            arr_clone[i] = array[i];
        return arr_clone;
    }

    /**
     * @description: 根据下标交换数组两个元素位置
     * @param {T} arr
     * @param {number} idx1
     * @param {number} idx2
     * @return {*} true 交换成功
     * @author: yayalee
     * @since: v1.0.0
     */
    public static swapArray<T>(arr: T[], idx1: number, idx2: number): boolean {
        if (idx1 === idx2 || !inRange(0, arr.length - 1, idx1) || !inRange(0, arr.length - 1, idx2)) {
            return false;
        }
        [arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]];
        return true;
    }

    /**
     * @description: 搜索数组中符合键值标准的第一个数据
     * @param {string} objectKey
     * @param {T} objectValue
     * @param {T} data
     * @param {T} defaultValue
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static searchArray<T>(objectKey: string, objectValue: T, data: T[] = [], defaultValue?: T | null): T {
        if (isArray(data)) {
            let index = data.findIndex((obj: any) => { return obj[objectKey] === objectValue; });
            if (index == -1) {
                return defaultValue!;
            }
            return data[index];
        }
        return defaultValue!;
    }

    /**
     * @description: 用户创建一个对某个数组进行映射的功能，该功能的作用是
     * 将某个数组的每个值变成一个新的值，保存到新数组
     * 交付新数组
     * @param {Array} array
     * @return {Function}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static map<T>(array: Array<T>): Function {
        return function (mapper: (arg0: T) => any): T[] {
            const newArr: T[] = [];
            for (let i = 0; i < array.length; i++) {
                const newItem = mapper(array[i]);
                newArr.push(newItem);
            }
            return newArr;
        }
    }

    /**
     * @description: 在一个数组中随机获取一个元素
     * @param {Array} array
     * @return {*}
     * @author: yayalee
     * @since: v1.0.0
     */
    public static randomArray<T>(array: Array<T>): T {
        var index: number = Math.floor(Math.random() * array.length);
        return array[index];
    }

}